﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Utilities;

namespace Geometry
{
   public class IdealHeightMap
   {
      public delegate bool CheckNeedsSplit(IdealTriangle triangle);

      ////////////////////////////////////////////////////////////////////////
      /// <summary>
      /// Height map must be square, with coordinates in the range [0, 2^n] so that it is
      /// easy to subdivide. If the true height map does not fit into this, then can
      /// build up from individual instances of the IdealHeightMap class
      /// </summary>
      public IdealHeightMap()
      {
         trees_ = new IdealTriangle[2];
      }

      ////////////////////////////////////////////////////////////////////////
      /// <summary>
      /// Get the height at the given location in the height map
      /// </summary>
      public float getHeight(UInt16 x, UInt16 y)
      {
         return heights_[x, y];
      }

      ////////////////////////////////////////////////////////////////////////
      /// <summary>
      /// The height map, dimensions of the float array must be a pure power of 2, e.g.
      /// 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024 ...
      /// 
      /// The 'x' ordinate runs from left to right and the 'y' ordinate runs from the
      /// camera into the screen, so that:
      /// 
      ///         z = heights[x, y];
      ///   
      /// </summary>
      public void setHeights(float[,] heights, UInt16 offsetX, UInt16 offsetY, UInt16 widthX, UInt16 widthY)
      {
         heights_ = heights;

         // Start with two triangles
         UInt16 maxX = (UInt16)(offsetX + widthX - 1);
         UInt16 maxY = (UInt16)(offsetY + widthY - 1);
         IdealVertex cornerA = new IdealVertex(offsetX, offsetY, heights_[offsetX, offsetY]);
         IdealVertex cornerB = new IdealVertex(maxX, offsetY, heights_[maxX, offsetY]);
         IdealVertex cornerC = new IdealVertex(maxX, maxY, heights_[maxX, maxY]);
         IdealVertex cornerD = new IdealVertex(offsetX, maxY, heights_[offsetX, maxY]);
         trees_[0] = new IdealTriangle(this, cornerA, cornerD, cornerC);
         trees_[1] = new IdealTriangle(this, cornerC, cornerB, cornerA);
         trees_[0].neighbourBase = trees_[1];
         trees_[1].neighbourBase = trees_[0];
      }

      ////////////////////////////////////////////////////////////////////////
      /// <summary>
      /// A delegate that returns true if the renderedHeight is sufficiently close to
      /// the trueHeight at the given point in space, that we don't need to split the
      /// element.
      /// </summary>
      public CheckNeedsSplit needSplitChecker = null;

      ////////////////////////////////////////////////////////////////////////
      /// <summary>
      /// Recalculate the mesh, splitting and merging elements until time
      /// runs out
      /// </summary>
      public void recalculate(DateTime timeLimit)
      {
         // Split any that need to be split
         trees_[0].checkSplit(timeLimit);
         trees_[1].checkSplit(timeLimit);
      }

      ////////////////////////////////////////////////////////////////////////
      //
      private void getTriangles(List<IdealVertex> result, IdealTriangle root)
      {
         if (null != root.childLeft)
         {
            getTriangles(result, root.childLeft);
            getTriangles(result, root.childRight);
         }
         else
         {
            //logger_.debugFormat("getTriangles() adding {0}", root);
            result.Add(root.vertexLeft);
            result.Add(root.vertexRight);
            result.Add(root.vertexApex);
         }
      }

      ////////////////////////////////////////////////////////////////////////
      //
      public IdealVertex[] getTriangles(int index)
      {
         List<IdealVertex> result = new List<IdealVertex>();
         getTriangles(result, trees_[index]);
         return result.ToArray();
      }

      ////////////////////////////////////////////////////////////////////////
      //
      private static Logger logger_ = new Logger();
      private float[,] heights_;
      private IdealTriangle[] trees_;
   }
}
